/**
 * 
 */
package com.toncen.samepms.util;

import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import ognl.Ognl;
import ognl.OgnlContext;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.toncen.samepms.common.AbstractDomain;
import com.toncen.samepms.common.BusinessRule;
import com.sky.common.annotation.AnnotationUtils;
import com.sky.common.ognl.OgnlUtils;
import com.sky.common.string.StringUtils;

/**
 * 属性业务规则工具类
 * 
 * @author tch
 * 
 */
public class BusinessRuleUtils {

	/**
	 * 日志记录
	 */
	private static Log log = LogFactory.getLog(BusinessRuleUtils.class);

	/**
	 * javabean 属性的get/set方法的关键字，用来匹配哪些方法是可以支持DataCell的，通过支持的分析得到属性名
	 */
	private static String[] JavaBeanGetSetKeys = { "get", "set", "is" };

	/**
	 * 获取必填规则验证失败元素集合
	 * 
	 * @param rootDomain
	 * @return
	 * @throws Exception
	 */
	public synchronized static List<String> getRequiredRules(AbstractDomain rootDomain) throws Exception {
		return getRequiredRules(rootDomain, null);
	}

	/**
	 * 获取必填规则验证失败元素集合
	 * 
	 * @param rootDomain
	 * @param otherDomain
	 * @return
	 * @throws Exception
	 */
	public synchronized static List<String> getRequiredRules(AbstractDomain rootDomain, AbstractDomain otherDomain) throws Exception {
		List<BusinessRuleWrap> rules = getRules(rootDomain, otherDomain);
		Object value;
		List<String> requireds = new ArrayList<String>();
		for (BusinessRuleWrap rule : rules) {
			value = BeanUtils.getProperty(rootDomain, rule.getName());
			if (StringUtils.isEmpty(value) && rule(rule.getRule(), rule.getRule().required(), rootDomain, otherDomain)) {
				requireds.add(rule.getRule().title());
			}
		}
		log.debug("[" + rootDomain.getClass().getName() + "]必须属性如下：" + requireds);
		return requireds;
	}

	/**
	 * 获取有效性规则验证失败元素集合
	 * 
	 * @param rootDomain
	 * @return
	 * @throws Exception
	 */
	public synchronized static List<String> getValidedRules(AbstractDomain rootDomain) throws Exception {
		return getValidedRules(rootDomain, null);
	}

	/**
	 * 获取有效性规则验证失败元素集合
	 * 
	 * @param rootDomain
	 * @param otherDomain
	 * @return
	 * @throws Exception
	 */
	public synchronized static List<String> getValidedRules(AbstractDomain rootDomain, AbstractDomain otherDomain) throws Exception {
		List<BusinessRuleWrap> rules = getRules(rootDomain, otherDomain);
		List<String> valideds = new ArrayList<String>();
		for (BusinessRuleWrap rule : rules) {
			if (!rule(rule.getRule(), rule.getRule().valided(), rootDomain, otherDomain)) {
				valideds.add(rule.getRule().title());
			}
		}
		log.debug("[" + rootDomain.getClass().getName() + "]无效属性如下：" + valideds);
		return valideds;
	}

	/**
	 * 获取全部业务规则
	 * 
	 * @param rootDomain
	 * @param otherDomain
	 * @return
	 * @throws Exception
	 */
	private static List<BusinessRuleWrap> getRules(AbstractDomain rootDomain, AbstractDomain otherDomain) throws Exception {
		List<Member> ruleFields = new ArrayList<Member>();
		AnnotationUtils.addAllMembers(BusinessRule.class, rootDomain.getClass(), ruleFields);
		BusinessRule rule;
		String name;
		Field field;
		Method method;
		List<BusinessRuleWrap> rules = new ArrayList<BusinessRuleWrap>();
		for (Member member : ruleFields) {
			if (member instanceof Field) {
				field = (Field) member;
				log.debug("业务规则检查字段【" + field.getName() + "】...");
				rule = field.getAnnotation(BusinessRule.class);
				name = rule.name();
				if (StringUtils.isEmpty(name)) {
					name = field.getName();
				}
			} else if (member instanceof Method) {
				method = (Method) member;
				log.debug("业务规则检查方法【" + method.getName() + "】...");
				rule = method.getAnnotation(BusinessRule.class);
				name = rule.name();
				if (StringUtils.isEmpty(name)) {
					name = method.getName();
					for (String key : JavaBeanGetSetKeys) {// 获取标准javabean的属性名称
						if (name.startsWith(key)) {
							name = name.replaceFirst(key, "");
							name = name.substring(0, 1).toLowerCase() + name.substring(1);
							break;
						}
					}
					if (method.getName().equals(name)) {
						throw new Exception("方法【" + name + "】不是标准的JavaBean的get/set方法格式，请与系统管理员联系！");
					}
				}
			} else {
				throw new Exception("未支持的成员类型【" + member.getClass().getName() + "】，请与系统管理员联系！");
			}
			rules.add(new BusinessRuleWrap(name, rule));
		}
		log.debug("[" + rootDomain.getClass().getName() + "]业务规则如下：" + rules);
		return rules;
	}

	/**
	 * @param rule
	 * @param expression
	 * @param domain
	 * @param otherDomain
	 * @return
	 * @throws Exception
	 */
	private static boolean rule(BusinessRule rule, String expression, AbstractDomain domain, AbstractDomain otherDomain) throws Exception {
		Object result;
		OgnlContext ognlContext = (OgnlContext) Ognl.createDefaultContext(null);
		if (otherDomain != null) {
			ognlContext.put(otherDomain.getClass().getSimpleName(), otherDomain);
		}
		result = OgnlUtils.getValue(expression, ognlContext, domain);
		if (result instanceof Boolean) {
			return (Boolean) result;
		} else {
			throw new Exception("业务对象【" + domain.getClass().getName() + "】属性【" + rule.title() + "】业务规则返回结果必须为类型【" + Boolean.class.getName() + "】，当前为【"
					+ (result == null ? "Null" : result.getClass().getName()) + "】，请与系统管理员联系！");
		}
	}

}

/**
 * @author tch
 * 
 */
class BusinessRuleWrap {
	/**
	 * 
	 */
	private String name;

	/**
	 * 
	 */
	private BusinessRule rule;

	public BusinessRuleWrap(String name, BusinessRule rule) {
		super();
		this.name = name;
		this.rule = rule;
	}

	/**
	 * @return the name
	 */
	public String getName() {
		return name;
	}

	/**
	 * @param name
	 *            the name to set
	 */
	public void setName(String name) {
		this.name = name;
	}

	/**
	 * @return the rule
	 */
	public BusinessRule getRule() {
		return rule;
	}

	/**
	 * @param rule
	 *            the rule to set
	 */
	public void setRule(BusinessRule rule) {
		this.rule = rule;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#toString()
	 */
	public String toString() {
		return name;
	}

}